package org.kernely.stream.persistence;

import java.util.HashSet;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.OneToMany;
import javax.persistence.Table;

import org.hibernate.annotations.Cascade;
import org.kernely.security.persistence.User;

/**
 * A stream contain messages. Users can write on it and subscribe to it.
 */
@Entity
@Table(name="kernely_stream")
public class Stream {
	/**
	 * Category for users' streams
	 */
	public static final String CATEGORY_USERS = "Streams/Users";
	/**
	 * Category for bundles' streams
	 */
	public static final String CATEGORY_BUNDLES = "Streams/Bundles";
	/**
	 * Category for other streams
	 */
	public static final String CATEGORY_OTHERS = "Streams/Others";
	
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	@Column(name="stream_id")
	/**
	 * Stream's id
	 */
	private int id;
	
	/**
	 * Stream's title
	 */
	private String title;

	/**
	 * Stream's category
	 */
	@Column(name="stream_category")
	private String category;

	/**
	 * Stream's state : locked or unlocked
	 */
	private int locked;
	
	/**
	 * Stream's unique Id
	 */
	@Column(name="stream_uid")
	private String uid;

	
	/**
	 * @return the state of the stream (true if locked, false otherwise).
	 */
	public final int getLocked() {
		return locked;
	}

	/**
	 * @param locked True if the stream must be locked, false otherwise.
	 */
	public final void setLocked(int locked) {
		this.locked = locked;
	}

	/**
	 * Users who subscribe to this stream
	 */
	@ManyToMany(cascade={CascadeType.PERSIST, CascadeType.MERGE}, fetch=FetchType.LAZY)
	@JoinTable( name="kernely_stream_subscription",
				joinColumns=@JoinColumn(name="fk_stream"),
				inverseJoinColumns=@JoinColumn(name="fk_user"))
	private Set<User> subscriptors;
	
	/**
	 * Users who can write to this stream
	 */
	@ManyToMany(cascade={CascadeType.PERSIST, CascadeType.MERGE}, fetch=FetchType.LAZY)
	@JoinTable( name="kernely_stream_redactors",
				joinColumns=@JoinColumn(name="fk_stream"),
				inverseJoinColumns=@JoinColumn(name="fk_user"))
	private Set<User> redactors;
	
	@OneToMany(fetch=FetchType.LAZY, mappedBy="stream")
	@Cascade({ org.hibernate.annotations.CascadeType.ALL, org.hibernate.annotations.CascadeType.DELETE_ORPHAN })
	private Set<Message> messages;

	/**
	 * Default constructor, which fill the stream with default values
	 */
	public Stream(){
		this.category = "";
		this.id = 0;
		this.locked = 0;
		this.messages = new HashSet<Message>();
		this.redactors = new HashSet<User>();
		this.subscriptors = new HashSet<User>();
		this.title="";
		this.uid = "";
	}
	
	/**
	 * @return the id
	 */
	public final int getId() {
		return id;
	}

	/**
	 * @param id the id to set
	 */
	public final void setId(int id) {
		this.id = id;
	}

	/**
	 * @return the title
	 */
	public final String getTitle() {
		return title;
	}

	/**
	 * @param title the title to set
	 */
	public final void setTitle(String title) {
		this.title = title;
	}

	/**
	 * @return the subscriptors
	 */
	public final Set<User> getSubscriptors() {
		return subscriptors;
	}

	/**
	 * @param subscriptors the subscriptors to set
	 */
	public final void setSubscriptors(Set<User> subscriptors) {
		this.subscriptors = subscriptors;
	}

	/**
	 * @return the redactors
	 */
	public final Set<User> getRedactors() {
		return redactors;
	}

	/**
	 * @param redactors the redactors to set
	 */
	public final void setRedactors(Set<User> redactors) {
		this.redactors = redactors;
	}

	/**
	 * @return the messages
	 */
	public final Set<Message> getMessages() {
		return messages;
	}

	/**
	 * @param messages the messages to set
	 */
	public final void setMessages(Set<Message> messages) {
		this.messages = messages;
	}

	/**
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public final int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + id;
		result = prime * result + ((title == null) ? 0 : title.hashCode());
		result = prime * result + ((uid == null) ? 0 : uid.hashCode());
		return result;
	}

	/**
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public final boolean equals(Object obj) {
		if (this == obj){
			return true;
		}
		if (obj == null){
			return false;
		}
		if (getClass() != obj.getClass()){
			return false;
		}
		Stream other = (Stream) obj;
		if (id != other.id){
			return false;
		}
		if (title == null) {
			if (other.title != null){
				return false;
			}
		} else if (!title.equals(other.title)){
			return false;
		}
		if (uid == null) {
			if (other.uid != null){
				return false;
			}
		} else if (!uid.equals(other.uid)){
			return false;
		}
		return true;
	}

	/**
	 * Gets the category
	 * @return the category
	 */
	public final String getCategory() {
		return category;
	}

	/**
	 * Sets the category
	 * @param category the category to set
	 */
	public final void setCategory(String category) {
		this.category = category;
	}

	/**
	 * Gets the uid
	 * @return the uid
	 */
	public final String getUid() {
		return uid;
	}

	/**
	 * Sets the uid
	 * @param uid the uid to set
	 */
	public final void setUid(String uid) {
		this.uid = uid;
	}

	/**
	 * Check if a user has subscribed to this stream.
	 * @param user The user.
	 * @return true if the user has subscribed, false otherwise.
	 */
	public final boolean isSubscribedBy(User user){
		return this.subscriptors.contains(user);
	}
	

}
